/*
 * ldap_protocol.c
 *
 *    Author: Matias Dumrauf
 * Revisions:
 *				 - News NetWork System         (16/04/2010).
 *				 - SadicO                      (02/11/2009).
 *				 - SOogle (Base Revision)      (01/07/2009).
 */

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <windows.h>
#include <string.h>
#include <ldap.h>
#include "ldap_protocol.h"
#include "utils.h"

static int startSession(HANDLE currentHeap, LDAP **ldap_session, t_ldap_context *ctx);
static int stopSession(LDAP **ldap_session);
static int modifyAttribute(LDAP **ldap_session, char *dn, char *attribute_name, char *new_attribute_value);
static int decodeAttributeName(char *attribute);

/* ----------------------------------- *
 *  Inclusion of deprecated functions  *
 * ----------------------------------- */
int ldap_simple_bind_s(LDAP *ld, char *who, char *passwd);
int ldap_unbind_s(LDAP *ld);


/* ------------------------------------------------------------------------------- *
 *                             LDAP Protocol Main Functions                        *
 * ------------------------------------------------------------------------------- */

/**
 *   NAME: ldap_protocol_createContext
 *   DESC: Crea un ldap context.
 * RETURN: Puntero a un Ldap Context.
 */
t_ldap_context   *ldap_protocol_createContext(HANDLE currentHeap, char *ip, int port, char *login_dn, char *password){
	t_ldap_context *new_ctx = (t_ldap_context*) HeapAlloc(currentHeap, HEAP_NO_SERIALIZE, sizeof(t_ldap_context));

	strcpy(new_ctx->ip, ip);
	new_ctx->port = port;
	strcpy(new_ctx->login_dn, login_dn);
	strcpy(new_ctx->password, password);
	return new_ctx;
}

/**
 * NAME: ldap_protocol_destroyContext
 * DESC: Destruye un ldap context.
 */
void              ldap_protocol_destroyContext(HANDLE currentHeap, t_ldap_context *ldap_ctx){
	HeapFree(currentHeap, HEAP_NO_SERIALIZE, ldap_ctx);
}

/* ------------------------------------------------------- */

/**
 *   NAME: ldap_protocol_newsgroup_add
 *   DESC: Agrega un nuevo Newsgroup con key=newsgroup_name.
 * RETURN: ok ? LDAP_SUCCESS : ldaperror
 */
int               ldap_protocol_newsgroup_add(HANDLE currentHeap, t_ldap_context *ctx, char newsgroup_name[]){
	LDAP *ldap_session = NULL;
	int   ldapresult;

    if( (ldapresult = startSession(currentHeap, &ldap_session, ctx)) != LDAP_SUCCESS )
    	return ldapresult;

    {
		char *dn = utils_string_buildFromTemplate(currentHeap, "%s=%s,%s", ATTRIBUTE_NEWSGROUP_NAME, newsgroup_name, BRANCH_NEWSNETWORK);

		char     *vclass[3], *vNewsgroupName[2], *vNumberOfNews[2], *aux;
		LDAPMod   modClass, modNewsgroupName, modNumberOfNews;
		LDAPMod  *mods[4];

		/* Se agregan estos objetos */
		modClass.mod_op                 =   LDAP_MOD_ADD;           /* Tipo de operacion */
		modClass.mod_type               =   ATTRIBUTE_OBJECT_CLASS; /* Tipo de Objeto a crear */
		vclass[0]                       =   OBJECT_CLASS_TOP;       /* Objeto1 */
		vclass[1]                       =   OBJECT_CLASS_NEWSGROUP; /* Objeto2 */
		vclass[2]                       =   NULL;                   /* Fin de declaraciones de objetos */
		modClass.mod_values             =   vclass;

		/* En los objetos se agregan los atributos que siguen */
		modNewsgroupName.mod_op         =   LDAP_MOD_ADD;
		modNewsgroupName.mod_type       =   ATTRIBUTE_NEWSGROUP_NAME;
		vNewsgroupName[0]               =   newsgroup_name;
		vNewsgroupName[1]               =   NULL;
		modNewsgroupName.mod_values     =   vNewsgroupName;

		modNumberOfNews.mod_op          =   LDAP_MOD_ADD;
		modNumberOfNews.mod_type        =   ATTRIBUTE_NUM_OF_NEWS;
		vNumberOfNews[0]                =   (aux = utils_convertIntToString(currentHeap, 0));
		vNumberOfNews[1]                =   NULL;
		modNumberOfNews.mod_values      =   vNumberOfNews;

		mods[0] = &modClass;
		mods[1] = &modNewsgroupName;
		mods[2] = &modNumberOfNews;
		mods[3] = NULL; /* Fin de ADDs */

		ldapresult = ldap_add_ext_s(ldap_session, dn, mods, NULL, NULL);
		HeapFree(currentHeap, HEAP_NO_SERIALIZE, dn);
		HeapFree(currentHeap, HEAP_NO_SERIALIZE, aux);
    }
	stopSession(&ldap_session);
	return ldapresult;
}

/**
 *   NAME: ldap_protocol_newsgroup_delete
 *   DESC: Elimina un Newsgroup acorde al newsgroup_name pasado como argumento.
 * RETURN: ok ? LDAP_SUCCESS : ldaperror
 */
int               ldap_protocol_newsgroup_delete(HANDLE currentHeap, t_ldap_context *ctx, char newsgroup_name[]){
	LDAP *ldap_session = NULL;
	int   ldapresult;
    char *dn;

    if( (ldapresult = startSession(currentHeap, &ldap_session, ctx)) != LDAP_SUCCESS )
    	return ldapresult;

    dn = utils_string_buildFromTemplate(currentHeap, "%s=%s,%s", ATTRIBUTE_NEWSGROUP_NAME, newsgroup_name, BRANCH_NEWSNETWORK);

	ldapresult = ldap_delete_ext_s(ldap_session, dn, NULL, NULL);
	stopSession(&ldap_session);
	HeapFree(currentHeap, HEAP_NO_SERIALIZE, dn);
	return ldapresult;
}

/**
 *   NAME: ldap_protocol_newsgroup_exists
 *   DESC: Consulta si existe 'newsgroup_name'.
 * RETURN: ok ? 1 : 0
 */
int               ldap_protocol_newsgroup_exists(HANDLE currentHeap, t_ldap_context *ctx, char newsgroup_name[]){
	LDAP        *ldap_session = NULL;
	int          ldapresult;
	char        *filter;
	LDAPMessage *searchResult;
	LDAPMessage *entry;

    if( (ldapresult = startSession(currentHeap, &ldap_session, ctx)) != LDAP_SUCCESS )
    	return ldapresult;

	filter = utils_string_buildFromTemplate(currentHeap, "(&(%s=%s)(%s=%s))", ATTRIBUTE_OBJECT_CLASS, OBJECT_CLASS_NEWSGROUP, ATTRIBUTE_NEWSGROUP_NAME, newsgroup_name);

	/* Search the directory */
	ldapresult = ldap_search_ext_s(ldap_session, BRANCH_NEWSNETWORK, LDAP_SCOPE_SUBTREE, filter,
								   NULL, 0, NULL, NULL, LDAP_NO_LIMIT, LDAP_NO_LIMIT, &searchResult);
	HeapFree(currentHeap, HEAP_NO_SERIALIZE, filter);

	if( ldapresult != LDAP_SUCCESS ){
		stopSession(&ldap_session);
		return 0;
	}
	ldapresult = (entry = ldap_first_entry(ldap_session, searchResult)) != NULL ? 1 : 0;
	ldap_msgfree(searchResult);
	stopSession(&ldap_session);
	return ldapresult;
}

/**
 *   NAME: ldap_protocol_newsgroup_select
 *   DESC: Selecciona un Newsgroup con key=newsgroup_name.
 *         Si existe, reserva memoria para una estructura 't_ldap_newsgroup',
 *         lo hace apuntar a 'newsGroupSelected' y carga los atributos.
 *         Sino, hace apuntar a 'newsGroupSelected' a NULL.
 *
 *   NOTA: En la linea 228, si se lo ejecuta desde el Visual Studio,
 *         tira un Break Point diciendo que hay un error en el heap, pero igual ejecuta bien.
 *         No tiene ningun error. Es un problema de la WinAPI. De echo, se libera correctamente el recurso 'bin_values'.
 *         Se puede ejecutar directamente desde el ejecutable y no tira ningun error.
 * RETURN: ok ? LDAP_SUCCESS : ldaperror
 */
int               ldap_protocol_newsgroup_select(HANDLE currentHeap, t_ldap_context *ctx, char newsgroup_name[], t_ldap_newsgroup **newsGroupSelected){
	LDAP        *ldap_session = NULL;
	int          ldapresult;
	char        *filter;
	LDAPMessage *searchResult;

    if( (ldapresult = startSession(currentHeap, &ldap_session, ctx)) != LDAP_SUCCESS ){
    	*newsGroupSelected = NULL;
    	return ldapresult;
    }

	filter = utils_string_buildFromTemplate(currentHeap, "(&(%s=%s)(%s=%s))", ATTRIBUTE_OBJECT_CLASS, OBJECT_CLASS_NEWSGROUP, ATTRIBUTE_NEWSGROUP_NAME, newsgroup_name);

    /* Search the directory */
	ldapresult = ldap_search_ext_s(ldap_session, BRANCH_NEWSNETWORK, LDAP_SCOPE_SUBTREE, filter,
								   NULL, 0, NULL, NULL, LDAP_NO_LIMIT, LDAP_NO_LIMIT, &searchResult);
	HeapFree(currentHeap, HEAP_NO_SERIALIZE, filter);

	if( ldapresult != LDAP_SUCCESS ){
		stopSession(&ldap_session);
		*newsGroupSelected = NULL;
		return ldapresult;
	}
	if( ldap_count_entries(ldap_session, searchResult) == 0 ){
		ldap_msgfree(searchResult);
		stopSession(&ldap_session);
		*newsGroupSelected = NULL;
		return ldapresult;
	}
	*newsGroupSelected = (t_ldap_newsgroup*)HeapAlloc(currentHeap, HEAP_NO_SERIALIZE, sizeof(t_ldap_newsgroup));
    {
		char           *attribute;
		struct berval **bin_values;
		BerElement     *ber;
		LDAPMessage    *entry;

		entry = ldap_first_entry(ldap_session, searchResult);

		/* Iterar los atributos de la entry */
		for(attribute = ldap_first_attribute(ldap_session, entry, &ber);
			attribute != NULL;
			attribute = ldap_next_attribute(ldap_session, entry, ber)){

			/* Se toma el valor por cada atributo */
			switch (decodeAttributeName(attribute)) {
				case IS_ATTRIBUTE_NEWSGROUP_NAME:
					bin_values = (struct berval**) ldap_get_values_len(ldap_session, entry, attribute);
					memcpy((*newsGroupSelected)->newsgroup_name, (bin_values[0])->bv_val, (bin_values[0])->bv_len);
					(*newsGroupSelected)->newsgroup_name[(bin_values[0])->bv_len] = '\0';
					HeapFree(currentHeap, HEAP_NO_SERIALIZE, bin_values[0]);
					ldap_memfree(bin_values);
					break;
				case IS_ATTRIBUTE_NUM_OF_NEWS:
					bin_values                     = ldap_get_values_len(ldap_session, entry, attribute);
					(*newsGroupSelected)->num_of_news = atoi((bin_values[0])->bv_val);
					HeapFree(currentHeap, HEAP_NO_SERIALIZE, bin_values[0]);
					ldap_memfree(bin_values);
					break;
				default:
					break;
			}
			ldap_memfree(attribute);
		}
		ldap_msgfree(entry);
		ber_free(ber, 0);
    }
	stopSession(&ldap_session);
	return ldapresult;
}

/**
 * NAME: ldap_protocol_newsgroup_destroy
 * DESC: Destruye una estructura t_ldap_newsgroup.
 */
void              ldap_protocol_newsgroup_destroy(HANDLE currentHeap, t_ldap_newsgroup *newsgroup){
	HeapFree(currentHeap, HEAP_NO_SERIALIZE, newsgroup);
}

/* ------------------------------------------------------- */

/**
 *   NAME: ldap_protocol_news_add
 *   DESC: Agrega una nueva Noticia en un Newsgroup con key1=newsgroup_name y key2=newsId.
 *   NOTA: El Newsgroup identificado con key=newsgroup_name debe existir y estar cargado.
 * RETURN: ok ? LDAP_SUCCESS : ldaperror
 */
int               ldap_protocol_news_add(HANDLE currentHeap, t_ldap_context *ctx, char newsgroup_name[], long newsId, char *head, char *body){
	LDAP             *ldap_session = NULL;
	int               ldapresult;
	t_ldap_newsgroup *newsgroup;
	char             *dn, *aux;

	if( (ldapresult = ldap_protocol_newsgroup_select(currentHeap, ctx, newsgroup_name, &newsgroup)) != LDAP_SUCCESS )
		return ldapresult;

	if( newsgroup == NULL ){
		printf("El Newsgroup: '%s' especificado no existe. Imposible cargar la News.\n", newsgroup_name);
		return ldapresult;
	}

    if( (ldapresult = startSession(currentHeap, &ldap_session, ctx)) != LDAP_SUCCESS ){
    	ldap_protocol_newsgroup_destroy(currentHeap, newsgroup);
    	return ldapresult;
    }
    {
		char      *vclass[3], *vNewsgroupName[2], *vNewsId[2], *vNewsHead[2], *vNewsBody[2];
		LDAPMod    modClass, modNewsgroupName, modNewsId, modNewsHead, modNewsBody;
		LDAPMod   *mods[6];

        dn = utils_string_buildFromTemplate(currentHeap, "%s=%d,%s=%s,%s",
                 ATTRIBUTE_NEWS_ID, newsId, ATTRIBUTE_NEWSGROUP_NAME, newsgroup_name, BRANCH_NEWSNETWORK);

		/* Se agregan estos objetos */
		modClass.mod_op                 =   LDAP_MOD_ADD;           /* Tipo de operacion */
		modClass.mod_type               =   ATTRIBUTE_OBJECT_CLASS; /* Tipo de Objeto a crear */
		vclass[0]                       =   OBJECT_CLASS_TOP;       /* Objeto1 */
		vclass[1]                       =   OBJECT_CLASS_NEWS;      /* Objeto2 */
		vclass[2]                       =   NULL;                   /* Fin de declaraciones de objetos */
		modClass.mod_values             =   vclass;

		/* En los objetos se agregan los atributos que siguen */
		modNewsgroupName.mod_op         =   LDAP_MOD_ADD;
		modNewsgroupName.mod_type       =   ATTRIBUTE_NEWSGROUP_NAME;
		vNewsgroupName[0]               =   newsgroup_name;
		vNewsgroupName[1]               =   NULL;
		modNewsgroupName.mod_values     =   vNewsgroupName;

		modNewsId.mod_op                 =  LDAP_MOD_ADD;
		modNewsId.mod_type               =  ATTRIBUTE_NEWS_ID;
		vNewsId[0]                       =  (aux = utils_convertLongToString(currentHeap, newsId));
		vNewsId[1]                       =  NULL;
		modNewsId.mod_values             =  vNewsId;

		modNewsHead.mod_op               =  LDAP_MOD_ADD;
		modNewsHead.mod_type             =  ATTRIBUTE_NEWS_HEAD;
		vNewsHead[0]                     =  head;
		vNewsHead[1]                     =  NULL;
		modNewsHead.mod_values           =  vNewsHead;

		modNewsBody.mod_op               =  LDAP_MOD_ADD;
		modNewsBody.mod_type             =  ATTRIBUTE_NEWS_BODY;
		vNewsBody[0]                     =  body;
		vNewsBody[1]                     =  NULL;
		modNewsBody.mod_values           =  vNewsBody;

		mods[0] = &modClass;
		mods[1] = &modNewsgroupName;
		mods[2] = &modNewsId;
		mods[3] = &modNewsHead;
		mods[4] = &modNewsBody;
		mods[5] = NULL; /* Fin de ADDs */

		ldapresult = ldap_add_ext_s(ldap_session, dn, mods, NULL, NULL);
		HeapFree(currentHeap, HEAP_NO_SERIALIZE, dn);
		HeapFree(currentHeap, HEAP_NO_SERIALIZE, aux);
	}
	/* Aumentar 'Number of News' del Newsgroup */
	dn = utils_string_buildFromTemplate(currentHeap, "%s=%s,%s", ATTRIBUTE_NEWSGROUP_NAME, newsgroup_name, BRANCH_NEWSNETWORK);
	modifyAttribute(&ldap_session, dn, ATTRIBUTE_NUM_OF_NEWS, aux = (utils_convertIntToString(currentHeap, ++(newsgroup->num_of_news))));
	HeapFree(currentHeap, HEAP_NO_SERIALIZE, dn);
	HeapFree(currentHeap, HEAP_NO_SERIALIZE, aux);
	ldap_protocol_newsgroup_destroy(currentHeap, newsgroup);
	stopSession(&ldap_session);
	return ldapresult;
}

/**
 *   NAME: ldap_protocol_news_delete
 *   DESC: Elimina una Noticia de un Newsgroup con key1=newsgroup_name y key2=newsId.
 * RETURN: ok ? LDAP_SUCCESS : ldaperror
 */
int               ldap_protocol_news_delete(HANDLE currentHeap, t_ldap_context *ctx, char newsgroup_name[], long newsId){
	LDAP *ldap_session = NULL;
	int   ldapresult;
    char *dn, *aux;

    t_ldap_newsgroup *newsgroup;

	if( (ldapresult = ldap_protocol_newsgroup_select(currentHeap, ctx, newsgroup_name, &newsgroup)) != LDAP_SUCCESS )
		return ldapresult;

	if( newsgroup == NULL ){
		printf("El Newsgroup: '%s' especificado no existe. Imposible eliminar la News.\n", newsgroup_name);
		return ldapresult;
	}

    if( (ldapresult = startSession(currentHeap, &ldap_session, ctx)) != LDAP_SUCCESS ){
    	ldap_protocol_newsgroup_destroy(currentHeap, newsgroup);
    	return ldapresult;
    }

    dn = utils_string_buildFromTemplate(currentHeap, "%s=%d,%s=%s,%s", ATTRIBUTE_NEWS_ID, newsId, ATTRIBUTE_NEWSGROUP_NAME, newsgroup_name, BRANCH_NEWSNETWORK);
	ldapresult = ldap_delete_ext_s(ldap_session, dn, NULL, NULL);
	HeapFree(currentHeap, HEAP_NO_SERIALIZE, dn);

	if( ldapresult != LDAP_SUCCESS ){
		ldap_protocol_newsgroup_destroy(currentHeap, newsgroup);
		return ldapresult;
	}
	/* Decrementar 'Number of News' del Newsgroup */
	dn = utils_string_buildFromTemplate(currentHeap, "%s=%s,%s", ATTRIBUTE_NEWSGROUP_NAME, newsgroup_name, BRANCH_NEWSNETWORK);
	modifyAttribute(&ldap_session, dn, ATTRIBUTE_NUM_OF_NEWS, aux = (utils_convertIntToString(currentHeap, --(newsgroup->num_of_news))));
	HeapFree(currentHeap, HEAP_NO_SERIALIZE, dn);
	HeapFree(currentHeap, HEAP_NO_SERIALIZE, aux);
	ldap_protocol_newsgroup_destroy(currentHeap, newsgroup);
	stopSession(&ldap_session);
	return ldapresult;
}

/**
 *   NAME: ldap_protocol_news_exists
 *   DESC: Consulta si existe la News correspondiente al 'newsId' del 'newsgroup_name'.
 * RETURN: ok ? 1 : 0
 */
int               ldap_protocol_news_exists(HANDLE currentHeap, t_ldap_context *ctx, char newsgroup_name[], long newsId){
	LDAP        *ldap_session = NULL;
	int          ldapresult;
	char        *filter;
	LDAPMessage *searchResult;
	LDAPMessage *entry;

    if( (ldapresult = startSession(currentHeap, &ldap_session, ctx)) != LDAP_SUCCESS )
    	return ldapresult;

    filter = utils_string_buildFromTemplate(currentHeap, "(&(%s=%s)(%s=%d))", ATTRIBUTE_NEWSGROUP_NAME, newsgroup_name, ATTRIBUTE_NEWS_ID, newsId);

	/* Search the directory */
	ldapresult = ldap_search_ext_s(ldap_session, BRANCH_NEWSNETWORK, LDAP_SCOPE_SUBTREE, filter,
								   NULL, 0, NULL, NULL, LDAP_NO_LIMIT, LDAP_NO_LIMIT, &searchResult);
	HeapFree(currentHeap, HEAP_NO_SERIALIZE, filter);

	if( ldapresult != LDAP_SUCCESS ){
		ldap_msgfree(searchResult);
		stopSession(&ldap_session);
		return 0;
	}
	ldapresult = (entry = ldap_first_entry(ldap_session, searchResult)) != NULL ? 1 : 0;
	ldap_msgfree(searchResult);
	stopSession(&ldap_session);
	return ldapresult;
}

/**
 *   NAME: ldap_protocol_news_select
 *   DESC: Selecciona una Noticia de un Newsgroup con key1=newsgroup_name y key2=newsId,
 *         y lo carga en la variable 'newsSelected'.
 *
 *   NOTA: En la linea 507, si se lo ejecuta desde el Visual Studio,
 *         tira un Break Point diciendo que hay un error en el heap, pero igual ejecuta bien.
 *         No tiene ningun error. Es un problema de la WinAPI. De echo, se libera correctamente el recurso 'bin_values'.
 *         Se puede ejecutar directamente desde el ejecutable y no tira ningun error.
 * RETURN: ok ? LDAP_SUCCESS : ldaperror
 */
int               ldap_protocol_news_select(HANDLE currentHeap, t_ldap_context *ctx, char newsgroup_name[], long newsId, t_ldap_news **newsSelected){
	LDAP           *ldap_session = NULL;
	int             ldapresult;
	char           *filter;
	LDAPMessage    *searchResult;

    if( (ldapresult = startSession(currentHeap, &ldap_session, ctx)) != LDAP_SUCCESS ){
    	*newsSelected = NULL;
    	return ldapresult;
    }

    filter = utils_string_buildFromTemplate(currentHeap, "(&(%s=%s)(%s=%d))", ATTRIBUTE_NEWSGROUP_NAME, newsgroup_name, ATTRIBUTE_NEWS_ID, newsId);

    /* Search the directory */
    ldapresult = ldap_search_ext_s(ldap_session, BRANCH_NEWSNETWORK, LDAP_SCOPE_SUBTREE, filter,
								   NULL, 0, NULL, NULL, LDAP_NO_LIMIT, LDAP_NO_LIMIT, &searchResult);
    HeapFree(currentHeap, HEAP_NO_SERIALIZE, filter);

    if( ldapresult != LDAP_SUCCESS ){
    	stopSession(&ldap_session);
    	*newsSelected = NULL;
    	return ldapresult;
    }
    if( ldap_count_entries(ldap_session, searchResult) == 0 ){
    	ldap_msgfree(searchResult);
    	stopSession(&ldap_session);
		*newsSelected = NULL;
		return ldapresult;
	}
    *newsSelected = (t_ldap_news*)HeapAlloc(currentHeap, HEAP_NO_SERIALIZE, sizeof(t_ldap_news));
    {
		char           *attribute;
		struct berval **bin_values;
		BerElement     *ber;
		LDAPMessage    *entry;

		entry = ldap_first_entry(ldap_session, searchResult);

		/* Iterar los atributos de la entry */
		for(attribute = ldap_first_attribute(ldap_session, entry, &ber);
			attribute != NULL;
			attribute = ldap_next_attribute(ldap_session, entry, ber)){

		    entry = ldap_first_entry(ldap_session, searchResult);

			/* Se toma el valor por cada atributo */
			switch (decodeAttributeName(attribute)) {
				case IS_ATTRIBUTE_NEWS_ID:
					bin_values = ldap_get_values_len(ldap_session, entry, attribute);
					(*newsSelected)->id = atol((bin_values[0])->bv_val);
					HeapFree(currentHeap, HEAP_NO_SERIALIZE, bin_values[0]);
					ldap_memfree(bin_values);
					break;
				case IS_ATTRIBUTE_NEWSGROUP_NAME:
					bin_values = (struct berval**) ldap_get_values_len(ldap_session, entry, attribute);
					memcpy((*newsSelected)->newsgroup_name, (bin_values[0])->bv_val, (bin_values[0])->bv_len);
					(*newsSelected)->newsgroup_name[(bin_values[0])->bv_len] = '\0';
					HeapFree(currentHeap, HEAP_NO_SERIALIZE, bin_values[0]);
					ldap_memfree(bin_values);
					break;
				case IS_ATTRIBUTE_NEWS_HEAD:
					bin_values = (struct berval**) ldap_get_values_len(ldap_session, entry, attribute);
					(*newsSelected)->head = (char*)HeapAlloc(currentHeap, HEAP_NO_SERIALIZE, (bin_values[0])->bv_len + 1);
					memcpy((*newsSelected)->head, (bin_values[0])->bv_val, (bin_values[0])->bv_len);
					(*newsSelected)->head[(bin_values[0])->bv_len] = '\0';
					HeapFree(currentHeap, HEAP_NO_SERIALIZE, bin_values[0]);
					ldap_memfree(bin_values);
					break;
				case IS_ATTRIBUTE_NEWS_BODY:
					bin_values = (struct berval**) ldap_get_values_len(ldap_session, entry, attribute);
					(*newsSelected)->body = (char*)HeapAlloc(currentHeap, HEAP_NO_SERIALIZE, (bin_values[0])->bv_len + 1);
					memcpy((*newsSelected)->body, (bin_values[0])->bv_val, (bin_values[0])->bv_len);
					(*newsSelected)->body[(bin_values[0])->bv_len] = '\0';
					HeapFree(currentHeap, HEAP_NO_SERIALIZE, bin_values[0]);
					ldap_memfree(bin_values);
					break;
				default:
					break;
			}
			ldap_memfree(attribute);
		}
		ldap_msgfree(entry);
		ber_free(ber, 0);
    }
	stopSession(&ldap_session);
	return ldapresult;
}

/**
 * NAME: ldap_protocol_news_destroy1
 * DESC: Destruye una puntero a una estructura t_ldap_news.
 */
void              ldap_protocol_news_destroy1(HANDLE currentHeap, t_ldap_news *news){
	HeapFree(currentHeap, HEAP_NO_SERIALIZE, news->head);
	HeapFree(currentHeap, HEAP_NO_SERIALIZE, news->body);
	HeapFree(currentHeap, HEAP_NO_SERIALIZE, news);
}

/**
 * NAME: ldap_protocol_news_destroy2
 * DESC: Destruye una estructura t_ldap_news.
 */
void              ldap_protocol_news_destroy2(HANDLE currentHeap, t_ldap_news *news){
	HeapFree(currentHeap, HEAP_NO_SERIALIZE, news->head);
	HeapFree(currentHeap, HEAP_NO_SERIALIZE, news->body);
}

/* ------------------------------------------------------------------------------- *
 *                          LDAP Protocol Private Functions                        *
 * ------------------------------------------------------------------------------- */

/**
 *   NAME: startSession
 *   DESC: Inicia una conexion a traves de Ldap.
 * RETURN: ok ? LDAP_SUCCESS : ldaperror
 */
static int startSession(HANDLE currentHeap, LDAP **ldap_session, t_ldap_context *ctx){
	int   version = LDAP_VERSION3, ldapresult;
	char *host = utils_string_buildFromTemplate(currentHeap, "ldap://%s:%d", ctx->ip, ctx->port);

	if( (ldapresult = ldap_initialize(ldap_session, host)) != LDAP_SUCCESS ){
		HeapFree(currentHeap, HEAP_NO_SERIALIZE, host);
		return ldapresult;
	}
	HeapFree(currentHeap, HEAP_NO_SERIALIZE, host);

	ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &version);
	return ldap_simple_bind_s(*ldap_session, ctx->login_dn, ctx->password);
}

/**
 *   NAME: stopSession
 *   DESC: Termina una conexion Ldap.
 * RETURN: ok ? LDAP_SUCCESS : ldaperror
 */
static int stopSession(LDAP **ldap_session){
	return ldap_unbind_s(*ldap_session);
}

/**
 *   NAME: modifyAttribute
 *   DESC: Modifica el valor de un atributo=attribute_name de una entry identificada con 'dn',
 *         y le coloca el nuevo 'attribute_newValue'.
 * RETURN: ok ? LDAP_SUCCESS : ldaperror
 */
static int modifyAttribute(LDAP **ldap_session, char *dn, char *attribute_name, char *new_attribute_value){
	LDAPMod *modify[2];
	LDAPMod  modAttribute;
	char    *vAttribute[2];

	modAttribute.mod_op     =   LDAP_MOD_REPLACE;
	modAttribute.mod_type   =   attribute_name;
	vAttribute[0]           =   new_attribute_value;
	vAttribute[1]           =   NULL;
	modAttribute.mod_values =   vAttribute;

    modify[0] = &modAttribute;
    modify[1] = NULL;

    return ldap_modify_ext_s(*ldap_session, dn, modify, NULL, NULL);
}

static int decodeAttributeName(char *attribute){
	if( !strcmp(attribute, ATTRIBUTE_OBJECT_CLASS) )       		return IS_ATTRIBUTE_OBJECT_CLASS;
	if( !strcmp(attribute, ATTRIBUTE_NEWSGROUP_NAME) ) 			return IS_ATTRIBUTE_NEWSGROUP_NAME;
	if( !strcmp(attribute, ATTRIBUTE_NUM_OF_NEWS) ) 			return IS_ATTRIBUTE_NUM_OF_NEWS;
	if( !strcmp(attribute, ATTRIBUTE_NEWS_ID) )       			return IS_ATTRIBUTE_NEWS_ID;
	if( !strcmp(attribute, ATTRIBUTE_NEWS_HEAD) )         		return IS_ATTRIBUTE_NEWS_HEAD;
	if( !strcmp(attribute, ATTRIBUTE_NEWS_BODY) )         		return IS_ATTRIBUTE_NEWS_BODY;
	/* else */
	return -1;
}
